package com.lenovo.lcdm.team.controller;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.EscapeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.lenovo.lcdm.common.enums.CommonMsgEnum;
import com.lenovo.lcdm.common.exception.ServiceException;
import com.lenovo.lcdm.common.model.ResponseVo;
import com.lenovo.lcdm.team.common.token.TokenSupport;
import com.lenovo.lcdm.team.common.util.ContextUtil;
import com.lenovo.lcdm.team.saml.SamlAuth;
import com.lenovo.lcdm.team.saml.SamlDto;
import com.lenovo.lcdm.team.service.ITeamUserService;
import com.lenovo.lcdm.team.service.LoginLogService;
import com.onelogin.saml2.settings.Saml2Settings;
import com.onelogin.saml2.util.Util;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import java.io.PrintWriter;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * Security adfs 登陆
 *
 * @author sunby1
 */
@Slf4j
@Controller
@RequestMapping("/adfs")
public class SamlLoginController {

    @Value("${spring.security.oauth2.client.provider.lcdm-oauth.token-uri}")
    private String url;

    @Value("${spring.security.oauth2.client.registration.oauth-client.client-id}")
    private String clientId;

    @Value("${spring.security.oauth2.client.registration.oauth-client.client-secret}")
    private String secret;

    @Value("${spring.security.oauth2.client.registration.oauth-client.authorization-grant-type}")
    private String grantType;

    @Value("${spring.security.oauth2.client.registration.oauth-client.scope}")
    private String scope;

    @Value("${lcdm.oauth.uri}")
    private String baseUrl;

    @Value("${lcdm.adfs.logoutUrl}")
    private String adfsLogoutUrl;

    @Value("${lcdm.front.error-auth-page}")
    private String errorAuthPage;

    @Value("${lcdm.front.main-page}")
    private String mainPage;

    @Value("${lcdm.adfs.entityId}")
    private String entityId;

    @Autowired
    private ITeamUserService teamUserService;

    @Autowired
    private LoginLogService loginLogService;


    @PostMapping("loginResult")
    public void loginResult(HttpServletRequest request, HttpServletResponse response) throws Exception {
        ResponseVo responseVo = new ResponseVo();
        String relayState = request.getParameter("RelayState");
        if (StrUtil.isBlank(relayState)) {
            log.error("SamlLoginController, relayState blank");
            throw new ServiceException("Adfs relayState is empty.");
        }

        SamlDto samlDto = new SamlDto();
        samlDto.setIdpEntityId(entityId);
        samlDto.setSpEntityId(entityId);
        samlDto.setSpAssertionConsumerServiceUrl(entityId);
        samlDto.setSpSingleLogoutServiceUrl(entityId);
        Saml2Settings saml2Settings = getSaml2Settings(samlDto);
        SamlAuth auth = new SamlAuth(saml2Settings, request, response);

        if (StrUtil.isNotBlank(relayState) && relayState.equalsIgnoreCase(saml2Settings.getIdpSingleLogoutServiceUrl().toString())) {
            log.error("loginResult: processing logout.");
            auth.processSLO();
            return;
        }

        auth.processResponse();
        if (!auth.isAuthenticated()) {
            log.error("SamlLoginController, auth  isAuthenticated not, auth:{}", auth);
            throw new ServiceException("Adfs login error.");
        }

        // 登录成功，权限处理，添加Token
        Map<String, List<String>> attributes = auth.getAttributes();
        List<String> itCodes = attributes.get("itcode");
        Optional<String> itCodeOpt = Optional.of(itCodes)
                .flatMap(lst -> lst.stream().filter(StrUtil::isNotBlank).findFirst());
        if (itCodeOpt.isEmpty()) {
            log.warn("adfs controller it code empty");
            response.sendRedirect(errorAuthPage);
            return;
        }

        relayState = StrUtil.isNotBlank(relayState) ? EscapeUtil.unescape(relayState) : mainPage;
        log.info("relayState:{}", relayState);
        String itcode = itCodeOpt.get();
        log.info("itcode:{}", itcode);
        log.info("url:{}", url);
        log.info("grantType:{}", grantType);
        HttpResponse httpResponse = HttpUtil.createPost(url)
                .basicAuth(clientId, secret)
                .form("grant_type", grantType)
                .form("itcode", itcode)
                .form("scope", scope)
                .execute();
        if (httpResponse.getStatus() == 401) {
            //记录登录失败日志
            loginLogService.loginFailedLog(request, itcode, "401");
            response.sendRedirect(errorAuthPage);
//            PrintWriter printWriter = response.getWriter();
//            responseVo.setCode(1000);
//            responseVo.setMessage(CommonMsgEnum.USER_NOT_EXIST.getMessage());
//            printWriter.print(JSONUtil.toJsonStr(responseVo));
//            printWriter.flush();
//            printWriter.close();
//            response.sendRedirect(relayState);
        } else {
            //如果是AD用户，更新userName
            List<String> displaynames = attributes.get("displayname");
            log.info("displaynames:{}", displaynames.toString());
            Optional<String> displaynameOpt = Optional.of(displaynames)
                    .flatMap(lst -> lst.stream().filter(StrUtil::isNotBlank).findFirst());
            if (!displaynameOpt.isEmpty()) {
                String displayname = displaynameOpt.get();
                log.info("displayname:{}", displayname);
                teamUserService.updateUserName(itcode, displayname);
            }
            //记录登录日志，更新最后登陆时间
            loginLogService.loginSuccessLog(request, itcode);

            String body = httpResponse.body();
            log.info("body:{}", body);
            JSONObject obj = JSONUtil.parseObj(body);
            if (relayState.contains(StringPool.QUESTION_MARK)) {
                log.info("url:{}", relayState + "&" + TokenSupport.TOKEN_HEADER_KEY + "=" + obj.get("token_type", String.class) + " " + obj.get("access_token", String.class));
                response.sendRedirect(relayState + "&" + TokenSupport.TOKEN_HEADER_KEY + "=" + obj.get("token_type", String.class) + " " + obj.get("access_token", String.class));
            } else {
                log.info("url:{}", relayState + "?" + TokenSupport.TOKEN_HEADER_KEY + "=" + obj.get("token_type", String.class) + " " + obj.get("access_token", String.class));
                response.sendRedirect(relayState + "?" + TokenSupport.TOKEN_HEADER_KEY + "=" + obj.get("token_type", String.class) + " " + obj.get("access_token", String.class));
            }
        }
    }

    @GetMapping("login")
    public void login(@RequestParam(value = "returnTo", required = false) String returnTo, HttpServletRequest request, HttpServletResponse response) throws Exception {
        log.info("AdfsLoginController login.");
        SamlDto samlDto = new SamlDto();
        samlDto.setIdpEntityId(entityId);
        samlDto.setSpEntityId(entityId);
        samlDto.setSpAssertionConsumerServiceUrl(entityId);
        samlDto.setSpSingleLogoutServiceUrl(entityId);
        Saml2Settings settings = getSaml2Settings(samlDto);
        SamlAuth auth = new SamlAuth(settings, request, response);
        auth.login(returnTo);
    }

    private Saml2Settings getSaml2Settings(SamlDto samlDto) throws Exception {
        Saml2Settings saml2Settings = BeanUtil.copyProperties(samlDto, Saml2Settings.class, "idpx509cert");
        try {
            saml2Settings.setIdpx509certMulti(Collections.singletonList(Util.loadCert((samlDto.getIdpx509cert()).trim())));
        } catch (Exception e) {
            throw new Exception(StrUtil.format("saml configuration idpx509cert is error"));
        }

        if (ObjectUtil.isNull(saml2Settings)) {
            throw new Exception(StrUtil.format("not select the saml configuration"));
        }
        return saml2Settings;
    }

}
